{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a12e75d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision.datasets import CIFAR10\n",
    "from torchvision.transforms import Compose, ToTensor, Normalize\n",
    "from torchvision.models import resnet18\n",
    "from tqdm.autonotebook import tqdm\n",
    "from copy import deepcopy"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bb2e232",
   "metadata": {},
   "source": [
    "## Setup DataLoader and Models \n",
    "\n",
    "An important detail is that although we are using the Validation set for `CIFAR10`, we **shuffle** and drop the last batch. This is to ensure that 1) the batches of each epoch are mixed, and 2) each iteration has the same batch size."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "47f97572",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Files already downloaded and verified\n"
     ]
    }
   ],
   "source": [
    "transforms = Compose([ToTensor(), \n",
    "                      Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])\n",
    "\n",
    "dataset = CIFAR10(root='./', train=False, download=True, transform=transforms)\n",
    "dataloader = DataLoader(dataset, batch_size=256, shuffle=True, drop_last=True, num_workers=4, pin_memory=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "48229bb6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dummy models created\n"
     ]
    }
   ],
   "source": [
    "model1 = resnet18(pretrained=True).cuda()\n",
    "model1.eval()\n",
    "model2 = deepcopy(model1)\n",
    "model2.eval()\n",
    "print('Dummy models created')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f59e737",
   "metadata": {},
   "source": [
    "## Compute CKA "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "5fed41fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "from cka import CKACalculator"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "926f66b6",
   "metadata": {},
   "source": [
    "### Basic Usage \n",
    "\n",
    "Initializing the `CKACalculator` object will add forward hooks to both `model1` and `model2`. \n",
    "The default modules that are hooked are: `Bottleneck`, `BasicBlock`, `Conv2d`, `AdaptiveAvgPool2d`, `MaxPool2d`, and all instances of `BatchNorm`. \n",
    "Note that `Bottleneck` and `BasicBlock` are from the `torchvision` implementation, and will not add hooks to any custom implementations of `Bottleneck/BasicBlock`.\n",
    "\n",
    "For ResNet18, a total of 50 hooks are added.\n",
    "\n",
    "By default, the intermediate features are flattened with `flatten_hook_fn` and 10 epochs are run."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d2212ba3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "No hook function provided. Using flatten_hook_fn.\n",
      "50 Hooks registered. Total hooks: 50\n",
      "No hook function provided. Using flatten_hook_fn.\n",
      "50 Hooks registered. Total hooks: 50\n"
     ]
    }
   ],
   "source": [
    "calculator = CKACalculator(model1=model1, model2=model2, dataloader=dataloader)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6282ebb3",
   "metadata": {},
   "source": [
    "Now we can calculate the CKA matrix "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e9525f51",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f2799b606e9248fca8f378a5f05633f8",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 0:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "60ce804cca4f49518bc3d8ddafdd9113",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 1:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "65ef31cacd8e4554848d26b5d24b61fe",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 2:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "450e5ff9bdea44b9b722221f20d0f0c9",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 3:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "5f28bdcb78fe4a9a93ac9039885348e6",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 4:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "587ac1386a9848789f5aa5b6fa54721e",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 5:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1e3f257f6c1c4da7874ad8b98a7d4404",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 6:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0384f3030fb048bf9bff23a16da18fe5",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 7:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "fec3f2bf2ff2486a8329308e4cf56307",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 8:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f2f2d8bf062b42eea94b6adfcbc1fa12",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 9:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CKA output size: torch.Size([50, 50])\n"
     ]
    }
   ],
   "source": [
    "cka_output = calculator.calculate_cka_matrix()\n",
    "print(f\"CKA output size: {cka_output.size()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75f4aedb",
   "metadata": {},
   "source": [
    "#### Visualize the output\n",
    "\n",
    "Note that the returned matrix has its origin at the top left. In most papers, the CKA matrix is visualized with its origin at the bottom left. Thus, we may flip the matrix first before visualization; however, this example chooses not to."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "2ae04fc9",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['figure.figsize'] = (7, 7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "1c7952cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7fcdc0045130>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 504x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(cka_output.cpu().numpy(), cmap='inferno')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8e6cab2",
   "metadata": {},
   "source": [
    "### Advanced Usage \n",
    "\n",
    "We can customize other parameters of the `CKACalculator`. \n",
    "Most importantly, we can select which modules to hook. \n",
    "\n",
    "Before instantiating a new instance of `CKACalculator` on, make sure to first call the `reset` method. \n",
    "This clears all hooks registered in the models."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "fe4a1711",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50 handles removed.\n",
      "50 handles removed.\n"
     ]
    }
   ],
   "source": [
    "# Reset calculator to clear hooks\n",
    "calculator.reset()\n",
    "torch.cuda.empty_cache()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "ff5c78e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn as nn"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f168ff41",
   "metadata": {},
   "source": [
    "Let's consider outputs of `Conv2d` and `BatchNorm2d` only. This will create 40 hooks.\n",
    "\n",
    "For custom layers, add the custom modules in the same manner as shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9e768045",
   "metadata": {},
   "outputs": [],
   "source": [
    "layers = (nn.Conv2d, nn.BatchNorm2d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "fd75a7d6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "No hook function provided. Using flatten_hook_fn.\n",
      "40 Hooks registered. Total hooks: 40\n",
      "No hook function provided. Using flatten_hook_fn.\n",
      "40 Hooks registered. Total hooks: 40\n"
     ]
    }
   ],
   "source": [
    "calculator = CKACalculator(model1=model1, model2=model2, dataloader=dataloader, hook_layer_types=layers)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "d27ee2fb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b70e3f403590421491fc7f829567f9e3",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 0:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "efbfb9df8ff94ea5a86b63f82c3cf4d9",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 1:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "96654df43385458f955ec4c9d0d5232b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 2:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "9e6dd64492ac41c68f9d54deb5771d8c",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 3:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "27775214034e4a988c1d625e476dedcf",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 4:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "dc962d2d294e45a5858dc9c6dae7a244",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 5:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "bb5baba5e69b4f43ba986e72485f4e4a",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 6:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "42b62b25a76d4c829c402c17e5517a66",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 7:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "16f8dfdba9794d4ca2bedc6a19f1bc48",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 8:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b23c62c491424afa90e488ba84099d3d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch 9:   0%|          | 0/39 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "cka_output = calculator.calculate_cka_matrix()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe47c21a",
   "metadata": {},
   "source": [
    "#### Visualize output "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ff47422c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7fcda46cfd60>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 504x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(cka_output.cpu().numpy(), cmap='inferno')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afaa68d2",
   "metadata": {},
   "source": [
    "#### Extract module names "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "b13097c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Layer 0: \tconv1\n",
      "Layer 1: \tbn1\n",
      "Layer 2: \tlayer1.0.conv1\n",
      "Layer 3: \tlayer1.0.bn1\n",
      "Layer 4: \tlayer1.0.conv2\n",
      "Layer 5: \tlayer1.0.bn2\n",
      "Layer 6: \tlayer1.1.conv1\n",
      "Layer 7: \tlayer1.1.bn1\n",
      "Layer 8: \tlayer1.1.conv2\n",
      "Layer 9: \tlayer1.1.bn2\n",
      "Layer 10: \tlayer2.0.conv1\n",
      "Layer 11: \tlayer2.0.bn1\n",
      "Layer 12: \tlayer2.0.conv2\n",
      "Layer 13: \tlayer2.0.bn2\n",
      "Layer 14: \tlayer2.0.downsample.0\n",
      "Layer 15: \tlayer2.0.downsample.1\n",
      "Layer 16: \tlayer2.1.conv1\n",
      "Layer 17: \tlayer2.1.bn1\n",
      "Layer 18: \tlayer2.1.conv2\n",
      "Layer 19: \tlayer2.1.bn2\n",
      "Layer 20: \tlayer3.0.conv1\n",
      "Layer 21: \tlayer3.0.bn1\n",
      "Layer 22: \tlayer3.0.conv2\n",
      "Layer 23: \tlayer3.0.bn2\n",
      "Layer 24: \tlayer3.0.downsample.0\n",
      "Layer 25: \tlayer3.0.downsample.1\n",
      "Layer 26: \tlayer3.1.conv1\n",
      "Layer 27: \tlayer3.1.bn1\n",
      "Layer 28: \tlayer3.1.conv2\n",
      "Layer 29: \tlayer3.1.bn2\n",
      "Layer 30: \tlayer4.0.conv1\n",
      "Layer 31: \tlayer4.0.bn1\n",
      "Layer 32: \tlayer4.0.conv2\n",
      "Layer 33: \tlayer4.0.bn2\n",
      "Layer 34: \tlayer4.0.downsample.0\n",
      "Layer 35: \tlayer4.0.downsample.1\n",
      "Layer 36: \tlayer4.1.conv1\n",
      "Layer 37: \tlayer4.1.bn1\n",
      "Layer 38: \tlayer4.1.conv2\n",
      "Layer 39: \tlayer4.1.bn2\n"
     ]
    }
   ],
   "source": [
    "for i, name in enumerate(calculator.module_names_X):\n",
    "    print(f\"Layer {i}: \\t{name}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
